//-----------------------------------------------------------------------
// <copyright file="CleanFolder.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <author>Mike Fourie</author>
// <email>v-mifour</email>
// <date>2008-03-01</date>
// <summary>
// This class implements the Task interface and is used to clean a Folder
//  This differs from the MSBUILD RemoveDir in that it doesn't remove the
//  folder, only the subfolder and all files in the parent
// </summary>
//-----------------------------------------------------------------------
namespace Microsoft.Sdc.Tasks.Folder
{
    using System;
    using System.IO;
    using Microsoft.Build.Framework;
    using System.Management;

    /// <summary>
    /// This class implements the Task interface and is used to clean a Folder
    /// This differs from the MSBUILD RemoveDir in that it doesn't remove the
    /// folder, only the subfolder and all files in the parent.
    /// 
    /// This tasks also supports shares
    /// </summary>
    /// <remarks>
    /// <code><![CDATA[<Folder.CleanFolder Path="path" Force="force"/>]]></code>
    /// <para>where:</para>
    /// <para><i>path (Required)</i></para>
    /// <para>
    /// The full path to the source folder that will be cleaned. This folder must exist.
    /// </para>
    /// <para><i>force</i></para>
    /// <para>If true, then ReadOnly files are made writable and deleted.</para>
    /// </remarks>
    /// <example>
    /// <code><![CDATA[
    /// <Project>
    ///     <Target Name="Test">
    ///         <Folder.CleanFolder Path="C:\SomeFolder"/>
    ///         <Folder.CleanFolder Path="\\server\\share" Force="true"/>
    ///     </Target>
    /// </Project>
    /// ]]></code>    
    /// </example>
    public class CleanFolder : TaskBase
    {
        private string path;
        private bool force;

        /// <summary>
        /// Gets or sets the path.
        /// </summary>
        /// <value>The path.</value>
        [Required]
        public string Path
        {
            get { return this.path; }
            set { this.path = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="CleanFolder"/> is force.
        /// </summary>
        /// <value><c>true</c> if force; otherwise, <c>false</c>.</value>
        public bool Force
        {
            get { return this.force; }
            set { this.force = value; }
        }

        /// <summary>
        /// Performs the action of this task.
        /// </summary>
        protected override void InternalExecute()
        {
            DirectoryInfo dir = new DirectoryInfo(this.Path);
            if (!dir.Exists)
            {
                Log.LogError(string.Format("The directory does not exist: {0}", this.Path));
                return;
            }

            FileSystemInfo[] infos = dir.GetFileSystemInfos("*");
            foreach (FileSystemInfo i in infos)
            {
                // Check to see if this is a DirectoryInfo object.
                if (i is DirectoryInfo)
                {
                    if (this.Force)
                    {
                        // if its a folder path we can use WMI for a quick delete
                        if (i.FullName.Contains(@"\\") == false)
                        {
                            string dirObject = string.Format("win32_Directory.Name='{0}'", i.FullName);
                            using (ManagementObject mdir = new ManagementObject(dirObject))
                            {
                                mdir.Get();
                                ManagementBaseObject outParams = mdir.InvokeMethod("Delete", null, null);

                                // ReturnValue should be 0, else failure
                                if (Convert.ToInt32(outParams.Properties["ReturnValue"].Value) != 0)
                                {
                                    Log.LogError(string.Format("Directory deletion error: ReturnValue: {0}", outParams.Properties["ReturnValue"].Value));
                                    return;
                                }
                            }
                        }
                        else
                        {
                            // it's a share, so we need to manually check all file attributes and delete
                            DelTree((DirectoryInfo)i);
                            Directory.Delete(i.FullName, true);
                        }
                    }
                    else
                    {
                        Directory.Delete(i.FullName, true);
                    }
                }
                else if (i is FileInfo)
                {
                    if (this.Force)
                    {
                        // First make sure the file is writable.
                        FileAttributes fileAttributes = File.GetAttributes(i.FullName);

                        // If readonly attribute is set, reset it.
                        if ((fileAttributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                        {
                            File.SetAttributes(i.FullName, fileAttributes ^ FileAttributes.ReadOnly);
                        }
                    }

                    File.Delete(i.FullName);
                }
            }
        }

        /// <summary>
        /// Dels the tree.
        /// </summary>
        /// <param name="root">The root.</param>
        private static void DelTree(DirectoryInfo root)
        {
            // Delete all files in current folder.
            foreach (FileInfo i in root.GetFiles())
            {
                // First make sure the file is writable.
                FileAttributes fileAttributes = File.GetAttributes(i.FullName);

                // If readonly attribute is set, reset it.
                if ((fileAttributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                {
                    File.SetAttributes(i.FullName, fileAttributes ^ FileAttributes.ReadOnly);
                }

                File.Delete(i.FullName);
            }

            foreach (DirectoryInfo d in root.GetDirectories())
            {
                DelTree(d);
                Directory.Delete(d.FullName);
            }
        }
    }
}